package com.relytosoft.mizar.media.camera.services.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.relytosoft.mizar.media.camera.api.pojo.dto.VideoRecordDto;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_AAC;
import static org.bytedeco.ffmpeg.global.avutil.AV_LOG_ERROR;

/**
 * @author wss
 * @version 1.0
 * @date 2022/6/16 10:06
 */
public interface StreamService {

    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            (int) (Runtime.getRuntime().availableProcessors() * 1.5),
            Runtime.getRuntime().availableProcessors() * 3,
            60L,
            TimeUnit.SECONDS,
            //选用无界的阻塞队列 则设置的mumPoolSize参数没有意义
            new LinkedBlockingDeque<Runnable>(10000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    String OUTPUT_PATH = "/relytosoft/mizar/media/file/";

    Logger log = LoggerFactory.getLogger(StreamService.class);

    Map<Integer, Boolean> STOP_MAP = new HashMap<>();

    /**
     * 构建采集器对象
     *
     * @param streamUrl
     * @return
     */
    default FFmpegFrameGrabber buildGrabber(String streamUrl) {
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(streamUrl);
        //tcp传输稳定
        grabber.setOption("rtsp_transport", "tcp");
        return grabber;
    }

    /**
     * 构建录制器对象
     *
     * @param filePath
     * @return
     */
    default FFmpegFrameRecorder buildRecorder(String filePath) {
        FileUtil.touch(filePath);
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(filePath, 1280, 720);
        //视频编码格式 此参数必须设置要不视频特别大
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        recorder.setFormat("avi");
        //视频包装格式
        recorder.setSampleRate(44100);
        //24帧
        recorder.setFrameRate(24);

        //video视频质量
        recorder.setVideoQuality(0);
        recorder.setVideoOption("crf", "23");
        //比特率速率
        recorder.setVideoBitrate(1000000);

        //视频质量和压缩率的权衡值
        recorder.setVideoOption("preset", "slow");
        recorder.setPixelFormat(0);
        //音频设置
        recorder.setAudioChannels(2);
        recorder.setPixelFormat(0);
        recorder.setAudioOption("crf", "0");
        recorder.setAudioQuality(0);
        recorder.setAudioCodec(AV_CODEC_ID_AAC);
        return recorder;
    }

    /**
     * 开始录制任务
     *
     * @param videoRecordDto
     * @return 任务id
     */
    default Integer recordByFrame(VideoRecordDto videoRecordDto) {
        //前置操作
        Integer taskId = frontOperate(videoRecordDto);
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        threadPoolExecutor.execute(() -> {
            RequestContextHolder.setRequestAttributes(attributes);
            FFmpegFrameGrabber grabber = null;
            FFmpegFrameRecorder recorder = null;
            try {
                grabber = this.buildGrabber(videoRecordDto.getStreamUrl());
                recorder = this.buildRecorder(videoRecordDto.getFilePath());
                avutil.av_log_set_level(AV_LOG_ERROR);
                grabber.start();
                recorder.start();
                log.info("{}---开始录制", videoRecordDto.getDeviceId());
                Frame frame;
                boolean status = true;
                videoRecordDto.setStartTime(DateUtil.currentSeconds());
                while (status && (frame = grabber.grabFrame()) != null) {
                    recorder.record(frame);
                    //中止操作
                    if (abortOperate(videoRecordDto)) {
                        status = false;
                    }
                }
                log.info("{}--结束录制", videoRecordDto.getDeviceId());
            } catch (Exception e) {
                log.info("{}--", "录制过程出现异常", e);
            } finally {
                try {
                    if (Objects.nonNull(grabber)) {
                        grabber.stop();
                        grabber.release();
                    }
                    if (Objects.nonNull(recorder)) {
                        recorder.stop();
                        recorder.release();
                    }
                    //后续操作
                    if (StrUtil.isNotBlank(videoRecordDto.getFilePath())) {
                        postOperate(videoRecordDto);
                    }
                } catch (FFmpegFrameGrabber.Exception | FFmpegFrameRecorder.Exception e) {
                    log.info("{}--", "关闭录取流失败", e);
                }
            }
        });
        return taskId;
    }

    /**
     * 前置操作
     */
    Integer frontOperate(VideoRecordDto videoRecordDto);

    /**
     * 后置操作
     */
    void postOperate(VideoRecordDto videoRecordDto);

    /**
     * 中止操作
     *
     * @param videoRecordDto
     * @return
     */
    Boolean abortOperate(VideoRecordDto videoRecordDto);

}
